package rev3;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;

import rev1.DHTUtil;
import rev3.MyString;
import rev3.NodeMyString;

public class NodeMyStringImpl implements NodeMyString{
	int id;
	HashMap<String, MyString> map;
	ArrayList<NodeMyString> fingers;
	
	
	public NodeMyStringImpl(int id)
	{
		this.id = id;
		map =  new HashMap<String, MyString>();
		fingers = new ArrayList<NodeMyString>();
	}
	
	
	public MyString get(String hsh) 
	{
		try {
			String sNodeHsh = DHTUtil.parseNodeHash(hsh);
			String sKey = DHTUtil.parseKey(hsh);
			
			if (sNodeHsh == null || sKey == null)
				throw new RuntimeException("Bad hsh in-parm");
			
			int findNodeHash = Integer.parseInt(sNodeHsh);

			NodeMyString lastFinger = fingers.get(fingers.size()-1);
			if (findNodeHash < id && findNodeHash >= lastFinger.getId())
				return lastFinger.get(hsh);

			NodeMyString prevFinger = fingers.get(0);
			
			for (int i = 1; i < fingers.size(); ++i) {
				NodeMyString nextFinger = fingers.get(i);
				
				if (findNodeHash >= prevFinger.getId() && findNodeHash < nextFinger.getId())
					return prevFinger.get(hsh);			
				
				prevFinger = nextFinger;
			}
			
			return map.get(sKey);
		} catch (NumberFormatException e) {
			System.out.println("ERROR in get():  ");
			e.printStackTrace();
		} catch (RemoteException e) {
			System.out.println("ERROR in get():  ");
			e.printStackTrace();
		}
		
		System.err.println("GET ERROR.  Node " + DHTUtil.parseNodeHash(hsh) + " not found.");
		return null;
	}

	public MyString put(String hash, MyString myStr)
	{
		try {
			String sNodeHsh = myStr.nodeId;
			String sKey = myStr.key;
			
			if (sNodeHsh == null || sKey == null)
				throw new RuntimeException("Bad hsh in-parm");
			
			int findNodeHash = Integer.parseInt(sNodeHsh);

			NodeMyString lastFinger = fingers.get(fingers.size()-1);
			NodeMyString prevFinger = fingers.get(0);
			if (id == findNodeHash)
				return map.put(sKey, myStr);  // Put in this chord
			else if (id < findNodeHash && findNodeHash <= prevFinger.getId())
				return prevFinger.put(sKey, myStr);  // Put in first chord
			else if (findNodeHash < id && findNodeHash >= lastFinger.getId())
				return lastFinger.put(sKey, myStr);  // Hand-off to last chord
	
			for (int i = 1; i < fingers.size(); ++i) {
				NodeMyString nextFinger = fingers.get(i);
				
				if (findNodeHash >= prevFinger.getId() && findNodeHash < nextFinger.getId())
					return prevFinger.put(sKey, myStr);			
				
				prevFinger = nextFinger;
			}
			
			return map.put(sKey, myStr);
		} catch (NumberFormatException e) {
			System.out.println("ERROR in get():  ");
			e.printStackTrace();
		} catch (RemoteException e) {
			System.out.println("ERROR in get():  ");
			e.printStackTrace();
		}
		
		System.err.println("PUT ERROR.  Unable to put for node="+myStr.nodeId);
		return null;

	}

	@Override
	public boolean addFinger(NodeMyString node) throws RemoteException {
		fingers.add(node);
		return true;
	}
	
	public int getId()
	{
		return id;
	}
	
}
